﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TanmiaGrp.GeneralLedger.IRepository.Basis;
using TanmiaGrp.Basis.Core;
using System.Data.Entity.Core.Objects;
using System.Diagnostics.Contracts;
using System.Collections;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Reflection;
using RefactorThis.GraphDiff;
using TanmiaGrp.GeneralLedger.Core;

namespace TanmiaGrp.GeneralLedger.SqlServerRepository
{
    public class DbRepository : IDbRepository
    {
        #region IDbRepository<BusinessObject> Members

        public virtual bool Create(BusinessObject entity)
        {
            Contract.Requires(entity != null, "BusinessObject entity parameter must not be null.");

            try
            {
                using (GLContext context = new GLContext())
                {
                    context.UpdateGraph<Account>(entity as Account);

                    TanmiaGrp.GeneralLedger.Core.Account aaa = entity as TanmiaGrp.GeneralLedger.Core.Account;
                    context.Accounts.Attach(aaa.Parent);
                    foreach (var item in aaa.AccountGroups)
                    {
                        context.Groups.Attach(item);
                    }

                    Type entityType = ObjectContext.GetObjectType(entity.GetType());
                    context.Set(entityType).Add(entity);

                    
                    DbEntityEntry[] entries = context.ChangeTracker.Entries().ToArray();
                    List<DbEntityEntry> toBeUnchanged = new List<DbEntityEntry>();

                    // retrieves all objects from all aggregation collection that represent many-to-many relationships
                    // Aggregation like Account->Groups, Truck->Shipments
                    IEnumerable<BusinessObject> result = from x in AssociationTypeAttribute.GetAssociationCollections(entity, AssociationTypeEnum.Aggregation)
                                                         from y in x
                                                         select y;

                    foreach (DbEntityEntry entry in entries)
                    {
                        // the root of aggregate root, must be marked as added.
                        if (entry.Entity == entity)
                        {
                            // to keep root object in Added state.
                            continue;
                        }

                        // if current entry represents an association to another businessEntity in entity
                        // association like Invoice->Customer, Account->ParentAccount
                        if (AssociationTypeAttribute.GetAssociationObjects(entity).Contains(entry.Entity))
                        {
                            toBeUnchanged.Add(entry);
                            continue;
                        }

                        // check if any of previous result is the same as current entry.Entity
                        // and mark that entity as Unchanged, because it is already created.
                        if (result.Any(x => x == entry.Entity))
                        {
                            toBeUnchanged.Add(entry);
                            continue;
                        }

                        entry.State = EntityState.Detached;
                    }

                    foreach (DbEntityEntry entry in toBeUnchanged)
                    {
                        entry.State = EntityState.Unchanged;
                    }

                    int erc = context.SaveChanges();
                    return erc > 0;
                }
            }
            catch (Exception ex)
            {
                throw ThrowHelper.ReThrow(ex);
            }
        }

        public bool Update(BusinessObject entity)
        {
            throw new NotImplementedException("Commented Intentionally");
            //try
            //{
            //    using (GLContext context = new GLContext())
            //    {
            //        Type entityType = ObjectContext.GetObjectType(entity.GetType());

            //        context.Set(entityType).Attach(entity);
            //        context.Entry(entity).State = System.Data.Entity.EntityState.Modified;

            //        foreach (var property in entity.GetAssociations(AssociationTypeEnum.Aggregation))
            //        {
            //            IEnumerable bizObjCollection = property.GetBusinessObjectCollection(entity);

            //            foreach (var item in bizObjCollection.GetStateList("Added"))
            //            {
            //                context.ObjectStateManager.ChangeRelationshipState(entity, item, property.Name, EntityState.Added);
            //            }

            //            foreach (var item in bizObjCollection.GetStateList("Removed"))
            //            {
            //                context.Set(item.GetType()).Attach(item);
            //                context.Entry(item).State = EntityState.Unchanged;

            //                context.ObjectStateManager.ChangeRelationshipState(entity, item, property.Name, EntityState.Deleted);
            //            }
            //        }

            //        int erc = context.SaveChanges();
            //        return erc > 0;
            //    }
            //}
            //catch (Exception ex)
            //{
            //    throw ThrowHelper.ReThrow(ex);
            //}
        }

        public virtual bool Delete(BusinessObject entity)
        {
            try
            {
                using (GLContext context = new GLContext())
                {
                    Type entityType = ObjectContext.GetObjectType(entity.GetType());

                    context.Set(entityType).Attach(entity);
                    context.Set(entityType).Remove(entity);

                    object oo = context.ChangeTracker.Entries().ToArray();

                    int erc = context.SaveChanges();
                    return erc > 0;
                }
            }
            catch (Exception ex)
            {
                throw ThrowHelper.ReThrow(ex);
            }
        }

        public virtual bool Delete(IEnumerable<BusinessObject> entities)
        {
            try
            {
                using (GLContext context = new GLContext())
                {
                    Type entityType = ObjectContext.GetObjectType(entities.GetType().GetGenericArguments()[0]);
                    entities.ToList().ForEach(x =>
                    {
                        context.Set(entityType).Attach(x);
                    });
                    context.Set(entityType).RemoveRange(entities);
                    int erc = context.SaveChanges();
                    return erc > 0;
                }
            }
            catch (Exception ex)
            {
                throw ThrowHelper.ReThrow(ex);
            }
        }

        #endregion
    }
}
